﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Globalization;
using JsonSharp;
using Barbar.MapReport.Common.Drawing;
using Microsoft.Practices.EnterpriseLibrary.Caching;
using JsonSharp.Collections;
using JsonSharp.Values;
using Barbar.MapReport.HtmlRender.Handlers;
using Barbar.MapReport.HtmlRender.Operations;
using System.Collections.ObjectModel;
using System.Web.UI;
using System.IO;
using Barbar.MapReport.Common;

namespace Barbar.MapReport.HtmlRender {
  internal class RenderingContext {
    private string m_CallbackId;
    private string m_MapObjectName;
    private string m_MapProxyUrl;
    private StringBuilder m_Builder = new StringBuilder();
    private bool m_IsCallbackRegistred = false;
    private Collection<string> m_LegendElements = new Collection<string>();

    public RenderingContext(string mapProxyUrl, string mapObjectName) {
      if (String.IsNullOrEmpty(mapObjectName))
        throw new ArgumentNullException("mapObjectName");

      m_CallbackId = Guid.NewGuid().ToString("N");
      m_MapObjectName = mapObjectName;
      m_MapProxyUrl = mapProxyUrl;

      m_Builder.AppendLine("var oContext;");
    }

    public string MapProxyUrl {
      get { return m_MapProxyUrl; }
    }

    public string CallbackId {
      get { return m_CallbackId; }
    }

    private void Write(JavaScriptFunction function, JsonValue argument, StringBuilder builder) {
      if (argument == null)
        builder.AppendLine(String.Format(CultureInfo.InvariantCulture, "{0}.{1}();", m_MapObjectName, function.FunctionName));
      else {
        builder.AppendLine(String.Format(CultureInfo.InvariantCulture, "oContext = {0}", argument));
        builder.AppendLine(String.Format(CultureInfo.InvariantCulture, "{0}.{1}(oContext);", m_MapObjectName, function.FunctionName));
      }
    }

    public void Write(JavaScriptFunction function, JsonValue argument) {
      Write(function, argument, m_Builder);
    }

    public string Content {
      get {
        if (m_IsCallbackRegistred) {
          StringBuilder resultBuilder = new StringBuilder(m_Builder.ToString());
          Write(JavaScriptFunction.TriggerMoveEnd, null, resultBuilder);
          return resultBuilder.ToString();
        }
        return m_Builder.ToString();
      }
    }

    public ReadOnlyCollection<string> LegendElements {
      get { return new ReadOnlyCollection<string>(m_LegendElements); }
    }

    internal void WriteCallbackOperation(IServerCallbackOperation callbackOperation) {
      if (!m_IsCallbackRegistred) {
        JsonObjects callbackArgument = new JsonObjects();
        callbackArgument.Add(new JsonStringValue("callbackUrl"), new JsonStringValue(new HandlerUrl(typeof(MapCallbackHandler)).ToString(MapProxyUrl)));
        callbackArgument.Add(new JsonStringValue("callbackId"), new JsonStringValue(CallbackId));
        callbackArgument.Add(new JsonStringValue("handlerUrl"), new JsonStringValue(MapProxyUrl));
        Write(JavaScriptFunction.RegisterServerCallback, callbackArgument);
        m_IsCallbackRegistred = true;
      }

      JsonObjects callbackData = new JsonObjects();
      callbackData.Add(new JsonStringValue("key"), new JsonStringValue(callbackOperation.CallbackService.GetType().FullName));

      bool dataStored = false;
      DataLocationInline inlineDataLocation = callbackOperation.DataLocation as DataLocationInline;
      if (inlineDataLocation != null) {
        callbackData.Add(new JsonStringValue("value"), new JsonStringValue(callbackOperation.CallbackService.Serialize(callbackOperation.Value)));
        dataStored = true;
      }

      DataLocationServer serverDataLocation = callbackOperation.DataLocation as DataLocationServer;
      if (serverDataLocation != null) {
        HtmlRenderCacheManager.AddCallbackItem(m_CallbackId, callbackOperation.CallbackService, callbackOperation.Value);
        dataStored = true;
      }

      if (!dataStored)
        throw new MapReportException("Unknown data store.");

      Write(JavaScriptFunction.SetServerCallbackData, callbackData);
    }

    internal void AddLegendElement(ILegendOperation legendOperation) {
      StringBuilder builder = new StringBuilder();
      using (StringWriter stringWriter = new StringWriter(builder)) {
        using (HtmlTextWriter writer = new HtmlTextWriter(stringWriter)) {
          legendOperation.RenderLegend().RenderControl(writer);
        }
      }
      m_LegendElements.Add(builder.ToString());
    }
  }
}
